﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	public sealed class IRT1T2Loop : IRT1T2Region
	{
		private readonly IRBasicBlock m_head;
		private readonly IRT1T2Block m_bbody;

		private readonly HashSet<long> m_containedblocks;

		private int m_priortoentryloc;
		private int m_looplastiterloc;

		/// <summary>
		/// If this loop has container traversal information associated with it
		/// </summary>
		private bool m_dolooptraverseadvance;

		/// <summary>
		/// If the traversal is on a container with then this is the root var 
		/// that we always access the container (or enumerator) from.
		/// </summary>
		private IRVarName m_containercopyvar;

		/// <summary>
		/// If the traversal is on an container with an indexer then 
		/// this is the optional member field that we always access the container from.
		/// </summary>
		private IRMemberField m_optcontainercopyfield;

		/// <summary>
		/// The unique operation that the indexer variable can me stored to at (i.e. check this)
		/// </summary>
		private IRStoreFromEvalStackBasic m_indexerstoreloc;

		public IRT1T2Loop(IRBasicBlock hb, IRT1T2Block loopstruct, HashSet<long> allblocks)
		{
			this.m_head = hb;
			this.m_bbody = loopstruct;
			this.m_containedblocks = allblocks;
			
			this.m_priortoentryloc = -1;
			this.m_looplastiterloc = -1;

			this.m_dolooptraverseadvance = false;
			this.m_containercopyvar = null;
			this.m_optcontainercopyfield = null;
			this.m_indexerstoreloc = null;
		}

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant(this.m_head != null);
		}

		public override String ToString()
		{
			StringBuilder sb = new StringBuilder();

			sb.Append("-----Loop @ Prior = " + this.m_priortoentryloc + " Past=" + this.m_looplastiterloc + "\n");

			if(this.m_dolooptraverseadvance)
				sb.Append("-----Container Traverse Loop-----\n");

			sb.Append("Head:\n");
			sb.Append(this.m_head.ToString());

			if(this.m_bbody != null)
			{
				sb.Append("-----Body:\n");
				sb.Append(this.m_bbody.ToString());
			}

			return sb.ToString();
		}

		public override void DGMLDisplayString(XmlWriter xwn, XmlWriter xwl, bool toplevel)
		{
			xwn.WriteStartElement("Node");
			xwn.WriteAttributeString("Id", this.GenDisplayNameString());
			xwn.WriteAttributeString("Label", "Loop -- [" + this.MinStmt().ToString() + ", " + this.MaxStmt().ToString() + "]" + (this.m_dolooptraverseadvance ? " TraverseLoop" : ""));

			if(!toplevel)
			{
				xwn.WriteAttributeString("Category", "Summary");
				xwn.WriteAttributeString("Group", "Collapsed");
			}

			xwn.WriteEndElement();

			this.m_head.DGMLDisplayString(xwn, xwl, false);

			if(!toplevel)
			{
				xwl.WriteStartElement("Link");
				xwl.WriteAttributeString("Source", this.GenDisplayNameString());
				xwl.WriteAttributeString("Target", this.m_head.GenDisplayNameString());
				xwl.WriteAttributeString("Category", "Contains");
				xwl.WriteEndElement();
			}

			if(this.m_bbody != null)
			{
				this.m_bbody.DGMLDisplayString(xwn, xwl, false);

				if(!toplevel)
				{
					xwl.WriteStartElement("Link");
					xwl.WriteAttributeString("Source", this.GenDisplayNameString());
					xwl.WriteAttributeString("Target", this.m_bbody.GenDisplayNameString());
					xwl.WriteAttributeString("Category", "Contains");
					xwl.WriteEndElement();
				}
			}
		}

		public override string GenDisplayNameString()
		{
			return "l" + this.m_head.GenDisplayNameString();
		}

		public override void NumberRegion(CtrGen cg)
		{
			this.m_priortoentryloc = cg.GenNextVal();
			this.m_looplastiterloc = cg.GenNextVal();

			this.m_head.NumberRegion(cg);

			if(this.m_bbody != null)
				this.m_bbody.NumberRegion(cg);
		}

		public int PriorToEntryUseModLoc
		{ get { return this.m_priortoentryloc; } }

		public int LoopLastIterUseModLoc
		{ get { return this.m_looplastiterloc; } }

		public override int MinStmt()
		{ return this.m_head.MinStmt(); }

		public override int MaxStmt()
		{ return (this.m_bbody != null ) ? this.m_bbody.MaxStmt() : this.m_head.MaxStmt(); }

		public bool IsSelfLoopOnHead 
		{ get { return this.m_bbody == null; } }

		public IRBasicBlock Head
		{ get { return this.m_head; } }
		
		public IRT1T2Block LoopBody 
		{ 
			get 
			{
				Contract.Requires(!this.IsSelfLoopOnHead);
				
				return this.m_bbody; 
			} 
		}

		public HashSet<long> AllContainedBlocks
		{ get { return this.m_containedblocks; } }

		/// <summary>
		/// If this loop has container traversal information associated with it
		/// </summary>
		public bool DoLoopTraverseOperations
		{ get { return this.m_dolooptraverseadvance; } }

		/// <summary>
		/// If the traversal is on a container with then this is the root var 
		/// that we always access the container (or enumerator) from.
		/// </summary>
		public IRVarName ContainerCopyVar
		{ get { return this.m_containercopyvar; } }

		/// <summary>
		/// If the traversal is on an container with an indexer then 
		/// this is the optional member field that we always access the container from.
		/// </summary>
		public IRMemberField OptContainerCopyField
		{ get { return this.m_optcontainercopyfield; } }

		/// <summary>
		/// The unique operation that the indexer variable can me stored to at (i.e. check this)
		/// </summary>
		public IRStoreFromEvalStackBasic IndexerStoreLoc
		{ get { return this.m_indexerstoreloc; } }

		internal override void PropLiveVariables(Dictionary<long, HashSet<IRVarName>> liveatexit, HashSet<IRVarName> addrtakenvars)
		{
			HashSet<IRVarName> lastliveenter = new HashSet<IRVarName>();
			do
			{
				lastliveenter.Clear();
				lastliveenter.UnionWith(liveatexit[this.m_head.BlockID]);

				if(this.m_bbody != null)
					this.m_bbody.PropLiveVariables(liveatexit, addrtakenvars);

				this.m_head.PropLiveVariables(liveatexit, addrtakenvars);

			} while(!lastliveenter.SetEquals(liveatexit[this.m_head.BlockID]));
		}

		internal override void ComputeEvalStackExpressions(Dictionary<long, SymbolicEvalStack> evsts, IRProgram p)
		{
			this.m_head.ComputeEvalStackExpressions(evsts, p);

			if(this.m_bbody != null)
				this.m_bbody.ComputeEvalStackExpressions(evsts, p);
		}

		internal override void DoLoopCopyAnalysis(bool findnewactions, long headblockid, long uniquebackblockid, IRProgram p, ref bool iterok, ref IRVarName iterator, HashSet<IRCallOp> itergets, ref IRCallOp enumadv, ref bool indexok, ref IRVarName indexvar, ref IRMemberField optfield, HashSet<IRByteCode> indexgets, ref IRVarName indexpos, ref IRStoreFromEvalStackBasic indexstore, Dictionary<long, IRBasicBlock> idtobbmap)
		{
			//if we are already looking for an action then we need to check on it
			if(!findnewactions)
			{
				iterok = false;
				iterator = null; 
				itergets.Clear();
				enumadv = null;
				indexok = false;
				indexvar = null;
				optfield = null;
				indexgets.Clear();
				indexpos = null;
				indexstore = null;
			}
			else
			{
				//if we are not already looking for an action then we can try to find a new one
				if(this.m_bbody != null)
				{
					List<long> idstol = new List<long>();
					foreach(long blckid in this.m_containedblocks)
					{
						IRBasicBlock bb = idtobbmap[blckid];
						List<uint> trgtl = new List<uint>();
						bb.GetAllBranchTargets(trgtl);
						if(trgtl.Contains((uint)this.m_head.BlockID))
							idstol.Add(blckid);
					}

					if(idstol.Count == 1)
						this.m_bbody.DoLoopCopyAnalysis(false, this.m_head.BlockID, idstol[0], p, ref iterok, ref iterator, itergets, ref enumadv, ref indexok, ref indexvar, ref optfield, indexgets, ref indexpos, ref indexstore, idtobbmap);
					else
					{
						iterok = false;
						indexok = false;
					}
				}

				this.m_head.DoLoopCopyAnalysis(false, this.m_head.BlockID, this.m_head.BlockID, p, ref iterok, ref iterator, itergets, ref enumadv, ref indexok, ref indexvar, ref optfield, indexgets, ref indexpos, ref indexstore, idtobbmap);
				
				if(iterok && enumadv != null)
				{
					this.m_dolooptraverseadvance = true;
					this.m_containercopyvar = iterator;

					foreach(IRCallOp cop in itergets)
					{
						cop.IsLoopTraverseGet = true;
						cop.ContainerCopyVar = this.m_containercopyvar;
					}

					enumadv.IsTraverseLoopIndexAdvance = true;
				}
				else if(indexok && indexstore != null)
				{
					this.m_dolooptraverseadvance = true;
					this.m_containercopyvar = indexvar;
					this.m_optcontainercopyfield = optfield;
					this.m_indexerstoreloc = indexstore;

					foreach(IRByteCode cop in indexgets)
					{
						if(cop is IRCallOp)
						{
							IRCallOp ccop = (IRCallOp)cop;
							ccop.IsLoopTraverseGet = true;
							ccop.ContainerCopyVar = this.m_containercopyvar;
							ccop.OptContainerCopyField = this.m_optcontainercopyfield;
							ccop.IndexerStoreLoc = this.m_indexerstoreloc;
						}
						else
						{
							IRLoadToEvalStackIndirect ilop = (IRLoadToEvalStackIndirect)cop;
							ilop.IsLoopTraverseGet = true;
							ilop.ContainerCopyVar = this.m_containercopyvar;
							ilop.OptContainerCopyField = this.m_optcontainercopyfield;
							ilop.IndexerStoreLoc = this.m_indexerstoreloc;
						}
					}

					indexstore.IsTraverseLoopIndexAdvance = true;
				}
				else
				{ 
					;
				}
			}
		}
	}
}
